package org.panxiong.openapi.handler;

import org.apache.maven.plugin.AbstractMojo;
import org.panxiong.openapi.model.*;
import org.panxiong.openapi.utils.ClassUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 对Controller类型对象处理
 */
public class ControllerHandler extends ControllerBase {

    // 类型列表
    private Set<Class<?>> classList;

    // API接口列表
    private Set<Class<?>> controllerClassList;

    // 解析后的列表
    private Set<ClassModel> classModelList;

    public ControllerHandler(AbstractMojo context, Set<Class<?>> classList) {
        super.context = context;
        this.classList = classList;
        this.controllerClassList = new HashSet<>();
        this.classModelList = new HashSet<>();
    }

    public Set<ClassModel> execute() throws Exception {
        for (Class<?> cls : classList) {
            Controller defController = cls.getAnnotation(Controller.class);
            RestController restController = cls.getAnnotation(RestController.class);
            if (defController != null || restController != null) {
                controllerClassList.add(cls);
            }
        }
        if (controllerClassList.size() > 0) {
            this.build();
        }

        return classModelList;
    }

    private void build() throws Exception {
        for (Class<?> cls : controllerClassList) {
            ClassModel cm = new ClassModel();
            cm.setName(buildClassName(cls));
            cm.setMapping(buildClassMapping(cls));
            cm.setMethodList(buildClassMethods(cls));
            cm.setRestController(cls.getAnnotation(RestController.class) != null);
            classModelList.add(cm);
        }
    }

    // ClassName
    private String buildClassName(Class<?> cls) {
        String className = null;
        Controller defController = cls.getAnnotation(Controller.class);
        if (defController != null) {
            className = defController.value();
        }
        if (defController == null || ClassUtils.isEmpty(className)) {
            RestController restController = cls.getAnnotation(RestController.class);
            if (restController != null) {
                className = restController.value();
            }
        }
        if (ClassUtils.isEmpty(className)) {
            className = cls.getSimpleName();
        }
        return className;
    }

    // ClassMapping
    private MappingMode buildClassMapping(Class<?> cls) throws Exception {
        MappingMode cmm = new MappingMode();
        RequestMapping requestMapping = cls.getAnnotation(RequestMapping.class);
        if (requestMapping != null) {
            super.setMappingModel(cmm, requestMapping);
        }
        return cmm;
    }

    // MethodList
    private List<MethodModel> buildClassMethods(Class<?> cls) throws Exception {
        List<MethodModel> methodModels = new ArrayList<>();
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method method : declaredMethods) {
            MethodModel model = new MethodModel();
            model.setName(method.getName());
            model.setMapping(super.buildMethodModelClassMapping(method));
            model.setContentType(super.getMethodContentType(method));
            model.setParameters(buildMethodParameters(method.getParameters()));
            model.setResult(buildMethodResult(method.getReturnType()).updateContentType(cls, method));
            methodModels.add(model);
        }
        return methodModels;
    }

    // ParamList
    private List<ParameterModel> buildMethodParameters(Parameter[] parameters) {
        List<ParameterModel> models = new ArrayList<>();
        for (Parameter parameter : parameters) {
            // model
            ParameterModel model = new ParameterModel();

            // json
            RequestBody requestBody = parameter.getAnnotation(RequestBody.class);
            if (requestBody != null) {
                model.setType(requestBody.annotationType().getSimpleName());
                model.setInfo(super.handlerTypeRequestBody(parameter, requestBody));
            }

            // form (表单对象)
            ModelAttribute modelAttribute = parameter.getAnnotation(ModelAttribute.class);
            if (modelAttribute != null) {
                model.setType(modelAttribute.annotationType().getSimpleName());
                model.setInfo(super.handlerTypeModelAttribute(parameter, modelAttribute));
            }

            // path
            PathVariable pathVariable = parameter.getAnnotation(PathVariable.class);
            if (pathVariable != null) {
                model.setType(pathVariable.annotationType().getSimpleName());

                FieldModel info = new FieldModel();
                info.setDateType(parameter.getType().getSimpleName());
                info.setMappingName(super.getAnnotationValue(pathVariable, parameter.getName()));
                info.setRequired(pathVariable.required());
                model.setInfo(info);
            }

            // param
            RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
            if (requestParam != null) {
                model.setType(requestParam.annotationType().getSimpleName());

                FieldModel info = new FieldModel();
                info.setDateType(parameter.getType().getSimpleName());
                info.setMappingName(super.getAnnotationValue(requestParam, parameter.getName()));
                info.setRequired(requestParam.required());
                info.setDefValue(requestParam.defaultValue());
                model.setInfo(info);
            }

            // part (文件上传)
            RequestPart requestPart = parameter.getAnnotation(RequestPart.class);
            if (requestPart != null) {
                model.setType(requestPart.annotationType().getSimpleName());

                FieldModel info = new FieldModel();
                info.setDateType(parameter.getType().getSimpleName());
                info.setMappingName(super.getAnnotationValue(requestPart, parameter.getName()));
                info.setRequired(requestPart.required());
                model.setInfo(info);
            }

            // header
            RequestHeader requestHeader = parameter.getAnnotation(RequestHeader.class);
            if (requestHeader != null) {
                model.setType(requestHeader.annotationType().getSimpleName());

                FieldModel info = new FieldModel();
                info.setDateType(parameter.getType().getSimpleName());
                info.setMappingName(super.getAnnotationValue(requestHeader, parameter.getName()));
                info.setRequired(requestHeader.required());
                info.setDefValue(requestHeader.defaultValue());
                model.setInfo(info);
            }

            // cookie
            CookieValue cookieValue = parameter.getAnnotation(CookieValue.class);
            if (cookieValue != null) {
                model.setType(cookieValue.annotationType().getSimpleName());

                FieldModel info = new FieldModel();
                info.setDateType(parameter.getType().getSimpleName());
                info.setMappingName(super.getAnnotationValue(cookieValue, parameter.getName()));
                info.setRequired(cookieValue.required());
                info.setDefValue(cookieValue.defaultValue());
                model.setInfo(info);
            }

            models.add(model);
        }
        return models;
    }

    private ResultModel buildMethodResult(Class<?> returnType) throws Exception {
        ResultModel model = new ResultModel();
        if (returnType == null || void.class.isAssignableFrom(returnType)) {
            model.setRequired(false);
        } else {
            model.setRequired(true);
            model.setDateType(returnType.getName());
            model.setContentType("");    //
            if (HttpEntity.class.isAssignableFrom(returnType) || ResponseEntity.class.isAssignableFrom(returnType)) {
                Class<?> generic = ClassUtils.getObjectGeneric(returnType);
                if (generic == null) {
                    model.setDescribed("结果经过泛型包装，无法读取类型描述");
                } else {
                    buildMethodResultInfo(model, generic);
                }
            } else if (ClassUtils.isJdkArray(returnType)) {
                Class<?> generic = ClassUtils.getObjectGeneric(returnType);
                if (generic == null) {
                    model.setDescribed("结果为泛型列表，无法读取类型描述");
                } else {
                    buildMethodResultInfo(model, generic);
                }
            } else if (ClassUtils.isJdkMap(returnType)) {
                model.setDescribed("结果为Map无法读取具体类型");
            } else if (ClassUtils.isBaseClass(returnType)) {
                model.setDescribed("结果为基本数据类型的单字段");
            } else {
                buildMethodResultInfo(model, returnType);
            }
        }
        return model;
    }

    private void buildMethodResultInfo(ResultModel model, Class<?> cls) {
        model.setStruct(new ArrayList<>());
        super.buildFieldWithChild(model.getStruct(), cls);
    }

}
